package com.autumn.util.security;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;

import com.autumn.util.ExceptionUtils;

/**
 * md5 帮助
 * 
 * @author shao
 * @date 2017/11/7 21:08
 */
public class Md5Utils {

	/**
	 * 16进制数组大写
	 */
	public final static char[] HEXDIGITS_ARRAY_LOWER = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'a', 'b', 'c', 'd', 'e', 'f' };

	/**
	 * 16进制数组大写
	 */
	public final static char[] HEXDIGITS_ARRAY_UPPER = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
			'A', 'B', 'C', 'D', 'E', 'F' };

	/**
	 * 16进制字符小写
	 */
	public final static String HEXDIGITS_STRING_LOWER = "0123456789abcdef";

	/**
	 * 16进制字符大写
	 */
	public final static String HEXDIGITS_STRING_UPPER = "0123456789ABCDEF";

	/**
	 * 采用utf-8生成 md5
	 * 
	 * @param content
	 *            内容
	 * @return
	 */
	public static final String md5(String content) {
		return md5(content, StandardCharsets.UTF_8);
	}

	/**
	 * 生成 md5
	 * 
	 * @param content
	 *            内容
	 * @param charset
	 *            编码
	 * @return 返回小写
	 */
	public static String md5(String content, Charset charset) {
		ExceptionUtils.checkNotNull(content, "content");
		if (charset == null) {
			charset = StandardCharsets.UTF_8;
		}
		try {
			byte[] e = content.getBytes(charset);
			MessageDigest mdInst = MessageDigest.getInstance("MD5");
			mdInst.update(e);
			byte[] md = mdInst.digest();
			int j = md.length;
			char[] str = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte b = md[i];
				str[k++] = HEXDIGITS_ARRAY_LOWER[b >>> 4 & 15];
				str[k++] = HEXDIGITS_ARRAY_LOWER[b & 15];
			}
			return new String(str);
		} catch (NoSuchAlgorithmException err) {
			throw ExceptionUtils.throwSystemException("包中无法加载 md5 算法。");
		}
	}

	/**
	 * 采用 utf - 8 进行 md5 签名验证
	 * 
	 * @param content
	 *            内容
	 * @param sign
	 *            签名
	 * @return
	 *
	 */
	public static boolean md5SginVerify(String content, String sign) {
		return (md5(content).equals(sign));
	}

	/**
	 * md5 签名验证
	 * 
	 * @param content
	 *            内容
	 * @param sign
	 *            签名
	 * @param charset
	 *            编码
	 * @return
	 *
	 */
	public static boolean md5SginVerify(String content, String sign, Charset charset) {
		return (md5(content, charset).equals(sign));
	}

	/**
	 * 获取流的Md5
	 * 
	 * @param input
	 *            输入
	 * @return
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 */
	public static String toMd5ByinputStream(InputStream input) throws IOException {
		return toMd5ByinputStream(input, null);
	}

	/**
	 * 获取流的Md5
	 * 
	 * @param input
	 *            输入
	 * @param copyOutput
	 *            复制输出
	 * @return
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 */
	public static String toMd5ByinputStream(InputStream input, ByteArrayOutputStream copyOutput) throws IOException {
		ExceptionUtils.checkNotNull(input, "input");
		MessageDigest digest;
		try {
			try {
				digest = MessageDigest.getInstance("MD5");
			} catch (NoSuchAlgorithmException e) {
				throw ExceptionUtils.throwSystemException("包中无法加载 md5 算法。");
			}
			byte[] buffer = new byte[4096];
			int length = -1;
			while ((length = input.read(buffer, 0, buffer.length)) != -1) {
				digest.update(buffer, 0, length);
				if (copyOutput != null) {
					copyOutput.write(buffer, 0, length);
				}
			}
			BigInteger bigInt = new BigInteger(1, digest.digest());
			return bigInt.toString(16);
		} finally {
			IOUtils.closeQuietly(input);
		}
	}
}
